// Needed for jsonDecode if used, though example uses Map directly

// Helper function for Employee example
Map<String, String> fetchDefaultData() {
  print('Fetching default data for Person...');
  // Simulate fetching data
  return {'name': 'Default Person'};
}

// --- 1. Point Class (Generative, Named, Redirecting, Initializer List, Assert) ---
class Point {
  double x; // Instance variables
  double y;

  // Generative constructor with initializing formal parameters
  Point(this.x, this.y) {
    print('Point generative constructor executed: ($x, $y)');
  }

  // Named constructor: Point.origin
  // Uses an initializer list to set final fields (if they were final)
  // Here, it just sets the values using an initializer list before the body runs.
  Point.origin() : x = 0, y = 0 {
    print('Point.origin named constructor executed: ($x, $y)');
  }

  // Redirecting constructor: Point.alongXAxis
  // Delegates to the main generative constructor `Point(this.x, this.y)`.
  // It CANNOT have a body {}. The print statement from the target constructor will run.
  Point.alongXAxis(double x) : this(x, 0); // Correct: No body allowed here

  // Named constructor with Initializer list from JSON
  // Note: The original example assumed x and y were final.
  // Here we adapt it for mutable x, y.
  // Using '!' assumes the keys 'x' and 'y' definitely exist and are doubles.
  Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']! {
    print('Point.fromJson named constructor executed: ($x, $y)');
  }

  // Constructor with assert in initializer list
  Point.withAssert(this.x, this.y) : assert(x >= 0, "x must be non-negative") {
    print('Point.withAssert constructor executed: ($x, $y)');
  }

  @override
  String toString() => 'Point($x, $y)';
}

// --- 2. ImmutablePoint Class (Constant Constructor) ---
class ImmutablePoint {
  // Compile-time constant instance
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x; // Instance variables must be final
  final double y;

  // Constant constructor
  const ImmutablePoint(this.x, this.y);

  @override
  String toString() => 'ImmutablePoint($x, $y)';
}

// --- 3. Logger Class (Factory Constructors) ---
class Logger {
  final String name;
  bool mute = false;

  // Private cache for instances
  static final Map<String, Logger> _cache = <String, Logger>{};

  // Factory constructor: Returns existing instance or creates a new one
  factory Logger(String name) {
    print('Logger factory constructor called for "$name"');
    return _cache.putIfAbsent(name, () {
      print('Creating new Logger instance for "$name"');
      return Logger._internal(name);
    });
  }

  // Factory constructor: Initializes from JSON (example logic)
  factory Logger.fromJson(Map<String, Object> json) {
    print('Logger.fromJson factory constructor called');
    // Delegates to the main factory constructor
    return Logger(json['name'].toString());
  }

  // Named private generative constructor used by the factory
  Logger._internal(this.name) {
    print('Logger._internal private constructor executed for "$name"');
  }

  void log(String msg) {
    if (!mute) print('$name: $msg');
  }
}

// --- 4. Listenable (Redirecting Factory Constructor Stub) ---
// Abstract base class
abstract class Listenable {
  // Redirecting factory constructor
  // It delegates the creation to the _MergingListenable constructor.
  factory Listenable.merge(List<Listenable> listenables) = _MergingListenable;

  void listen(); // Abstract method
}

// Concrete implementation used by the redirecting factory
class _MergingListenable implements Listenable {
  final List<Listenable> _listenables;

  // Constructor for the concrete class
  _MergingListenable(this._listenables) {
    print(
      '_MergingListenable constructor executed with ${_listenables.length} listenables.',
    );
  }

  @override
  void listen() {
    print('Merging listenable is listening...');
    // Actual merging logic would go here
  }
}

// Simple implementation for demonstration
class SimpleListenable implements Listenable {
  final String id;
  SimpleListenable(this.id);

  @override
  void listen() {
    print('SimpleListenable $id is listening.');
  }
}

// --- 5. Instance Variable Initialization Examples ---

// Initialization at declaration
class PointA {
  double x = 1.0; // Initialized here
  double y = 2.0; // Initialized here

  // Implicit default constructor PointA(); uses these initial values.

  @override
  String toString() => 'PointA($x, $y)';
}

// Initialization using initializing formal parameters
class PointB {
  final double x; // Final variables must be initialized
  final double y;

  // Initializing formals (required positional)
  PointB(this.x, this.y);

  // Optional initializing formals with default values (POSITIONAL)
  PointB.optional([this.x = 0.0, this.y = 0.0]);

  @override
  String toString() => 'PointB($x, $y)';
}

// Demonstrating private fields (cannot use initializing formal for named private)
class PointWithPrivate {
  final double _x; // Private fields
  final double _y;

  // Named constructor initializing private fields via initializer list
  PointWithPrivate.namedPrivate({required double x, required double y})
    : _x = x,
      _y = y;

  @override
  String toString() => 'PointWithPrivate($_x, $_y)';
}

// Initialization using named initializing formal parameters
class PointC {
  double x;
  double y;

  // Named initializing formals with default values (NAMED)
  PointC.named({this.x = 1.0, this.y = 1.0});

  @override
  String toString() => 'PointC.named($x, $y)';
}

// Nullable variables don't strictly need initialization
class PointD {
  double? x; // Nullable, defaults to null
  double? y; // Nullable, defaults to null

  // Initializing formals (can still be used)
  PointD(this.x, this.y);

  @override
  String toString() => 'PointD($x, $y)';
}

// --- 6. Constructor Inheritance Examples ---

// Superclass
class Person {
  String name;

  // Unnamed constructor
  // Person(this.name) {
  //   print('Person unnamed constructor: $name');
  // }

  // Named constructor used by subclass
  Person.fromJson(Map<String, String> data) : name = data['name']! {
    print('Person.fromJson named constructor: $name');
  }
}

// Subclass
class Employee extends Person {
  // Employee() : super('Default Employee Name') { // Call super unnamed constructor
  //   print('Employee default constructor');
  // }

  // Must call a superclass constructor if superclass has no unnamed, no-arg constructor
  // Or if you want to call a specific one.
  Employee() : super.fromJson(fetchDefaultData()) {
    print('Employee constructor calling super.fromJson');
  }

  // Example calling a specific super constructor (if Person had an unnamed one)
  // Employee.specific(String name) : super(name) {
  //   print('Employee.specific constructor');
  // }
}

// --- 7. Super Parameters Example ---

class Vector2d {
  final double x;
  final double y;

  // Generative constructor
  Vector2d(this.x, this.y) {
    print('Vector2d constructor: ($x, $y)');
  }

  // Named constructor
  Vector2d.named({required this.x, required this.y}) {
    print('Vector2d.named constructor: (x: $x, y: $y)');
  }
}

class Vector3d extends Vector2d {
  final double z;

  // Using super parameters to forward x and y to the default Vector2d constructor
  Vector3d(super.x, super.y, this.z) {
    print('Vector3d constructor: ($x, $y, $z)'); // x and y are accessible
  }

  // Using super parameters with a named super constructor
  // Forwards 'y' using super parameter, explicitly sets 'x' in super call
  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0) {
    print('Vector3d.yzPlane constructor: (x: $x, y: $y, z: $z)');
  }

  // Example of what NOT to do (causes error):
  // Vector3d.xAxisError(super.x): z = 0, super(0); // BAD: Tries to set x twice
}

// --- Main Function to Demonstrate Constructors ---
void main() {
  print('--- Demonstrating Point Constructors ---');
  var p1 = Point(1, 2);
  var p2 = Point.origin();
  var p3 = Point.alongXAxis(5); // Calls Point(5, 0) constructor
  var p4 = Point.fromJson({'x': 3.5, 'y': 4.5});
  try {
    var p5 = Point.withAssert(10, 20);
    print('Assertion passed for $p5');
    // This will throw an assertion error in debug mode
    // var p6 = Point.withAssert(-1, 5);
  } catch (e) {
    print(
      'Caught error: $e',
    ); // Catches assertion error if not in debug mode (less likely)
  }
  print('');

  print('--- Demonstrating Constant Constructor ---');
  // Creates compile-time constants
  const imPoint1 = ImmutablePoint(1, 1);
  const imPoint2 = ImmutablePoint(1, 1); // Same object as imPoint1
  var imPoint3 = const ImmutablePoint(2, 2); // Also constant
  print('imPoint1: $imPoint1');
  print('imPoint2: $imPoint2');
  print('imPoint3: $imPoint3');
  print(
    'imPoint1 identical to imPoint2: ${identical(imPoint1, imPoint2)}',
  ); // true
  print('Using static constant: ${ImmutablePoint.origin}');
  print('');

  print('--- Demonstrating Factory Constructors (Logger) ---');
  var logger1 = Logger('UI'); // Creates new instance
  logger1.log('Button clicked');
  var logger2 = Logger('Network'); // Creates new instance
  var logger3 = Logger('UI'); // Returns cached instance for 'UI'
  print('logger1 identical to logger3: ${identical(logger1, logger3)}'); // true

  var logMap = {'name': 'AppCore'};
  var loggerJson = Logger.fromJson(logMap); // Uses factory fromJson
  loggerJson.log('Initialized from JSON');
  print('');

  print('--- Demonstrating Redirecting Factory Constructor (Listenable) ---');
  var l1 = SimpleListenable('A');
  var l2 = SimpleListenable('B');
  var merged = Listenable.merge([l1, l2]); // Calls the redirecting factory
  merged.listen();
  print('');

  print('--- Demonstrating Constructor Tear-Offs ---');
  var charCodes = [68, 97, 114, 116]; // ASCII for D, a, r, t
  // Use tear-off for named constructor
  var strings = charCodes.map(String.fromCharCode).toList();
  print('Tear-off String.fromCharCode: $strings');
  // Use tear-off for unnamed constructor
  var buffers = charCodes.map(
    StringBuffer.new,
  ); // Creates an Iterable<StringBuffer>
  // Let's realize it and add the code
  var bufferList =
      buffers.map((b) {
        b.writeCharCode(
          charCodes[buffers.toList().indexOf(b)],
        ); // A bit contrived to show usage
        return b.toString();
      }).toList();
  print('Tear-off StringBuffer.new (processed): $bufferList');
  print('');

  print('--- Demonstrating Instance Variable Initialization ---');
  var pointA = PointA();
  print('PointA (initialized at declaration): $pointA');

  var pointB1 = PointB(3, 4);
  print('PointB (initializing formals): $pointB1');
  // Corrected: Use positional argument for optional positional parameter
  var pointB2 = PointB.optional(5); // x is 5, y defaults to 0.0
  print('PointB (optional initializing formals): $pointB2');

  var pointPvt = PointWithPrivate.namedPrivate(x: 10, y: 20);
  print('PointWithPrivate (initializer list for private): $pointPvt');

  var pointC1 = PointC.named(); // Uses defaults
  print('PointC (named initializing formals, default): $pointC1');
  var pointC2 = PointC.named(y: 2.5); // x defaults to 1.0
  print('PointC (named initializing formals, specific): $pointC2');

  var pointD1 = PointD(null, 5.5);
  print('PointD (nullable initializing formals): $pointD1');
  var pointD2 = PointD(1.1, 2.2);
  print('PointD (nullable initializing formals): $pointD2');
  print('');

  print('--- Demonstrating Constructor Inheritance ---');
  var emp = Employee();
  print('Employee created: ${emp.name}');
  print('');

  print('--- Demonstrating Super Parameters ---');
  var v3d_1 = Vector3d(1.0, 2.0, 3.0); // Uses positional super params
  print('Vector3d instance 1 created.');
  var v3d_2 = Vector3d.yzPlane(
    y: 4.0,
    z: 5.0,
  ); // Uses named super param and super.named call
  print('Vector3d instance 2 created.');
  print('');

  print('--- All Demonstrations Complete ---');
}
